\documentclass[12pt,final]{article}
\usepackage{latexsym}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}
\DeclareGraphicsExtensions{.pdf,.png,.jpg}
\frenchspacing
\usepackage{minted}
\usepackage[russian]{babel}

\def\SM{\emph{Simulation Modeling} }

\begin{document}
\author{Altaisoft}
\title{Simulation modeling}

\maketitle

\section{Введение}

Программа \SM предназначена для выполнения лабораторных работ по курсу ''Имитационное моделирование экономических процессов'' в Томском университете систем управления и радиоэлектроники.

\emph{Просто добавь воды}

\section{Модели}

\subsection{Склад}

\subsubsection{Постановка задачи}
В нашем распоряжении находится склад некого товара (пусть это будет топливо для АЭС). Время от времени (в среднем каждые \texttt{demand} дней) к нам приходят покупатели, которые желают купить по урановому блочку каждый по определённой цене \texttt{demand\_price}. Мы имеем перед ними договорные обязательства, -- поэтому, если в данный момент у нас топлива нет, то мы платим неудовлетворённому покупателю неустойку в размере \texttt{fine}.

Чтобы пополнять свои запасы, мы можем обращаться к поставщикам -- заводам-переработчикам сырья, заказывая у них блочки партиями. Каждый блочок вместе с доставкой стоит \texttt{supply\_price}. Задача усложняется тем, что время доставки каждой партии -- случайная величина. Положим, что в среднем партия доставляется за \texttt{supply} дней.

Мы бы могли заказать сразу заведомо слишком большое количество блочков и таким образом наверняка выполнить свои обязательства, но хранение каждого блочка требует ресурсов и обходится в \texttt{storage\_price} в расчёте на один блочок в сутки. Размер склада мы в текущей версии модели, следуя учебно-методическому пособию, считаем бесконечным.

Нам необходимо определить, когда заказывать партии блочков и каков должен быть размер каждой партии, чтобы мы удовлетворили как можно больше покупателей и наша прибыль была максимальна.

Наша модель должна помочь в решении этой задачи.

\subsubsection{Основные параметры}

Подбор оптимального решения мы будем проводить, подстраивая два параметра модели:

\begin{description}
    \item[\texttt{lot\_size}] -- размер каждой партии;
    \item[\texttt{limit}] -- минимальный остаток на складе. Если запас блочков опускается величины \texttt{limit}, то наша автоматическая система управления складом отправляет поставщику заказ на новую партию.
\end{description}

Текущее количество блочков на складе мы будем хранить в переменной \texttt{amount}. Итак, вот наши входные переменные:

\begin{description}
    \item[\texttt{supply} и \texttt{demand}] -- потоки заказов и заявок. Каждый из них является генератором положительных чисел с нормальным (гауссовым) распределением.
    \item[\texttt{amount} и \texttt{limit}] -- текущее количество блочков на складе и минимально допустимое их количество.
    \item[\texttt{supply\_price}, \texttt{demand\_price}, \texttt{storage\_price} и \texttt{fine}] -- цена покупки, цена продажи, цена хранения и неустойка в пользу покупателя.
    \item[\texttt{lot\_size}] -- размер каждой партии.
    \item[\texttt{total\_time}] -- общая продолжительность моделирования.
\end{description}

\subsubsection{Алгоритм}

В жизни нашего виртуального склада может произойти лишь два события: прибытие покупателя или новой партии топлива. Поскольку первое происходит чаще, будем ориентироваться на него.

Как мы помним, \texttt{demand} -- это последовательность пауз между прибытием покупателей. Иными словами, если он вернул нам поочерёдно 1 и 3, это значит, что первый покупатель придёт на следующий день после начала работы склада, а второй пожалует ещё через три дня (на четвёртый день), и так далее.

Мы обозначим паузу как \texttt{delta} и будем проходить этой переменной по всей последовательности \texttt{demand}. На каждом шаге цикла переменная \texttt{time} будет обозначать текущее время.

\begin{minted}{python}
    time = 0
    for delta in demand:
        time += delta
        
        # <...>
\end{minted}

Вместо многоточия должна стоять обработка вновь совершённой покупки.

Однако мы забыли о том, что время от времени к нам приходят новые партии топлива, и если такая партия пришла перед вновь совершаемой покупкой, нам необходимо её учесть на складе, прежде чем эту покупку проводить. Пусть время доставки партии хранится в переменной \texttt{delivery}. Тогда, если \texttt{delivery} $<=$ \texttt{time}, сначала следует обработать прибытие партии, а затем уже покупку.

\begin{minted}{python}
    never = total_time + 1
    
    time = 0
    for delta in demand:
        time += delta
        
        if delivery <= time:
            amount += lot_size
            delivery = never
        
        # <...>
\end{minted}

Значение \texttt{never} означает, что на данный момент у поставщика обязательств перед нами нет -- все наши заказы уже удовлетворены.

Теперь рассмотрим, как будет обрабатываться покупка (то, что сейчас обозначено  как многоточие). Имеются следующие три случая.

\begin{description}
    \item[На складе нет продукции.] Мы разводим руками -- придётся платить неустойку.
    \item[На складе есть продукция.] Мы отдаём один блочок покупателю и он уходит довольным.
    \begin{description}
        \item[Оставшийся запас не превышает лимита.] Отправляем заводу-поставщику заказ на новую партию, если только мы сейчас не ожидаем прибытия уже сделанного заказа.
    \end{description}
\end{description}

Это легко записывается в виде следующего кода.

\begin{minted}{python}
        if amount:
            amount -= 1
            
            if amount <= limit and delivery > total_time:
                delivery = time + supply.next()
\end{minted}

Описанный нами цикл является бесконечным. Необходимо гарантировать, что время \texttt{time} никогда не превысит \texttt{total\_time} -- тогда алгоритм будет в любом случае завершаться. И последнее -- необходимо добавить в модель сбор статистики и вывод результатов моделирования.

Результат можно посмотреть в файле \texttt{models/warehouse.py}.

Однако это ещё не всё. Обратим внимание на тот факт, что прибытие партии будет обработано лишь в том случае, если после него следует хотя бы одна покупка. В обратном случае партия не будет учтена -- как будто её и не было, что может в определённых условиях исказить результаты работы. Поэтому ещё один блок обработки партии мы добавим после завершения цикла.

\begin{minted}{python}
if delivery <= total_time:
    amount += lot_size
    supplies += 1
    average += (total_time - delivery) * lot_size
    delivery = never
\end{minted}

\subsubsubsection{Среднее количество товара на складе}

Переменная \texttt{average} хранит математическое ожидание среднего количества товара на складе, помноженное на \texttt{total\_time}. Необходимо доказать, что она считается корректно. Вытащив из кода все фрагменты, которые касаются этой переменной, можно записать:

\begin{minted}{python}
    average = 0
    for delta in demand:
        time += delta
        
        if time > total_time:
            time -= delta
            break
        
        average += amount * delta
        
        if delivery <= time:
            average += (time - delivery) * lot_size
    
    average += (total_time - time) * amount
    
    if delivery < total_time:
        average += (total_time - delivery) * lot_size
\end{minted}

Изначально \texttt{average} равна нулю. При каждом событии покупки к ней добавляется величина $\Delta * amount$, где $\Delta$ есть количество времени с прошлой покупки (или с начала моделирования). Кроме того, в конце алгоритма к этой величине добавляется мат. ожидание количества товара в промежуток между последней покупкой и концом времени моделирования. Таким образом, к концу алгоритма имеем:

\begin{equation*}
    average = \int_{t=0}^{total\_time} amount(t)
\end{equation*}

(если не учитывать заказы).

Когда в момент \texttt{delivery} приходит заказ, к \texttt{average} добавляется его объём, помноженный на время между этим заказом и последующей покупкой, корректируя таким образом значение \texttt{average}.

Нельзя это рассуждение считать строгим доказательством, но тем не менее.

\subsubsection{Тесты}

Алгоритм достаточно простой и представляется очевидным. Но его необходимо протестировать. Тесты содержатся в файле \texttt{tests/warehouse\_tests.py}. Необходимо написать достаточно тестов, чтобы покрыть все наиболее важные случаи. По каким признакам можно разделить эти случаи?

\begin{itemize}
    \item По отсутствию или наличию поставок;
    \item По отсутствию или наличию дефицита и соответственно неустоек.
\end{itemize}

Спрос -- по предусловию -- отсутствовать не может.





\end{document}
